#include "OgreCharacter.h"
#include "CollidingObject.h"

using namespace Ogre;
using namespace OgreOde;

OgreCharacter::OgreCharacter( String name, OgreOde::World *world)
{
	// Setup basic member references
	mName = name;
    mWorld = world;
	mSceneMgr = world->getSceneManager();
    mWorld->setCollisionListener(this);
    mSpace = world->getDefaultSpace();

	// Setup basic node structure to handle 3rd person cameras
	mMainNode = mSceneMgr->getRootSceneNode ()->createChildSceneNode (mName);
	mSightNode = mMainNode->createChildSceneNode (mName + "_sight", Vector3 (0, 0, 100));
	mCameraNode = mMainNode->createChildSceneNode (mName + "_camera", Vector3 (0, 50, -100));

	_thrust_offs = Ogre::Vector3(0,0.1,0);
    _thrust = 50.0;
	
	speed = 1000;
	boost = 1;
	shoot_timer = 0;

	boost = 0;
    i=0;
//mSpace = mWorld->getDefaultSpace();
    const Ogre::Real _time_step = 0.5;
    const Ogre::Real time_scale = Ogre::Real(1.7);
    const Ogre::Real max_frame_time = Ogre::Real(1.0 / 4);
    mStepper = new OgreOde::StepHandler(mWorld, OgreOde::StepHandler::QuickStep,_time_step, max_frame_time,
    time_scale);

/* ROBOTTT 2 */
    mEntity = mSceneMgr->createEntity("ent" + mName,"razor.mesh");
    mEntity->setQueryFlags (1<<2);
    mMainNode->attachObject(mEntity);
    mEntity->setNormaliseNormals(true);
    mEntity->setCastShadows(true);
    mBody2 = new OgreOde::Body(mWorld, "player_0");
    mMainNode->attachObject(mBody2);
    Vector3 size(50.0,50.0,50.0);
    OgreOde::BoxMass mMass(2.5,size);
    mMass.setDensity(5.0,size);
    mBody2->setMass(mMass);
    mGeom2 = (OgreOde::Geometry*)new OgreOde::BoxGeometry(size, mWorld, mSpace);
    mMainNode->setScale(size.x * 0.02,size.y * 0.02,size.z * 0.02);
    mGeom2->setBody(mBody2);
    mEntity->setUserObject(mGeom2);
    mGeom2->setUserObject(static_cast<CollisionTestedObject*>(this));
    mBody2->setPosition(Vector3(400,520,-20));


	powerup_counter = 0;
	lives = 5;

}

OgreCharacter::~OgreCharacter()
{
	mMainNode->detachAllObjects ();
	delete mEntity;
	mMainNode->removeAndDestroyAllChildren ();
	mSceneMgr->destroySceneNode (mName);
}

void OgreCharacter::update(Real elapsedTime, OIS::Keyboard* input, OIS::Mouse* mouse)
{
	using namespace OIS;
    //mMainNode->translate (mMainNode->getOrientation () * Vector3 (0, 0, 150 * elapsedTime));
   // mBody2->setForce(mBody2->getOrientation()*Ogre::Vector3(0,0,5*elapsedTime));
    //mBody2->setPosition(mBody2->getPosition()*Ogre::Vector3(0,0,5*elapsedTime));
	// Handle movement

	while(!delete_list.empty())
	{
		try {
			OgreOde::Geometry *del = delete_list.front();
			OgreOde::Body *body = del->getBody();
			Ogre::String name = body->getName();
	
			if (name.compare(0,5,"enemy") == 0)
			{
				enemy_manager->delete_enemy(del);
			}
			else if (name.compare(0,4,"beam") == 0)
			{
				mSceneMgr->destroySceneNode(name);
				body->~ Body();
				del->~ Geometry();
			}
			delete_list.pop_front();
		} 
		catch (...) 
		{ 
		//u dont have the node already registered in the scene manager 
		}
	}

	if (boost_timer > 0 ) {
		boost_timer -= elapsedTime;
	} else {
		boost = 1;
	}
    mBody2->setLinearVelocity(mBody2->getOrientation()*Ogre::Vector3(0,0,speed*boost*elapsedTime));

    _thrust_force = Ogre::Vector3::ZERO;
    if (input->isKeyDown(KC_G)) _thrust_force.z += _thrust;
    if (input->isKeyDown(KC_B)) _thrust_force.z -= _thrust;
    if (input->isKeyDown(KC_V)) mBody2->addRelativeTorque(Vector3(0,5000*elapsedTime,0));
    if (input->isKeyDown(KC_N)) mBody2->addRelativeTorque(Vector3(0,-5000*elapsedTime,0));

// Apply the thrust force
        mBody2->addForceAt(_thrust_force,_thrust_offs);

	/*if (input->isKeyDown (OIS::KC_Q))
	{
		//mMainNode->translate (mMainNode->getOrientation () * Vector3 (0, 0, 150 * elapsedTime));
   //     mBody2->setPosition(mBody2->getOrientation()*Ogre::Vector3(0,0,0.5));
        Quaternion q = mBody2->getOrientation();
        mBody2->setAngularVelocity(q*Ogre::Vector3(10*cos(1),0,10*sin(1)));

	} else {
      //  mBody2->setPosition(mBody2->getOrientation()*Ogre::Vector3(0,0,0.1));
        Quaternion q = mBody2->getOrientation();
        mBody2->setAngularVelocity(q*Ogre::Vector3(10*cos(1),0,10*sin(1)));
    }*/

	//	mMainNode->translate (mMainNode->getOrientation () * Vector3 (0, 0, -50 * elapsedTime));

	if (input->isKeyDown (OIS::KC_W))
	{
		//mBody2->addForce(Ogre::Vector3(0,-10*elapsedTime,0));
        //mBody2->addForce(mBody2->getOrientation()*Ogre::Vector3(5*elapsedTime,0,0));
        //  OgreOde::Body* torso = mNode->getObject("mName");
        Quaternion q1 = mBody2->getOrientation();
        Quaternion q2(Radian (-1  * elapsedTime),Ogre::Vector3::UNIT_X);
        mBody2->setOrientation(q1*q2);
        mBody2->setForce(mBody2->getOrientation()*mBody2->getForce());
	}
	if (input->isKeyDown (OIS::KC_S)) 
	{
        Quaternion q1 = mBody2->getOrientation();
        Quaternion q2(Radian (1 * elapsedTime),Ogre::Vector3::UNIT_X);
        mBody2->setOrientation(q1*q2);
        mBody2->setForce(mBody2->getOrientation()*mBody2->getForce());
	}
    if (input->isKeyDown( OIS::KC_A)) 
    {
      //  OgreOde::Body* torso = mNode->getObject("mName");
        Quaternion q1 = mBody2->getOrientation();
        Quaternion q2(Radian (-2 * elapsedTime),Ogre::Vector3::UNIT_Z);
        mBody2->setOrientation(q1*q2);

    }
    if (input->isKeyDown( OIS::KC_D)) 
    {
      //  OgreOde::Body* torso = mNode->getObject("mName");
        Quaternion q1 = mBody2->getOrientation();
        Quaternion q2(Radian (2 * elapsedTime),Ogre::Vector3::UNIT_Z);
        mBody2->setOrientation(q1*q2);
        mBody2->setForce(mBody2->getOrientation()*mBody2->getForce());
    } 

/*	if( input->isKeyDown( OIS::KC_Q ) ) mMainNode->pitch (Radian (-2 * elapsedTime));

    //mMainNode->translate( mMainNode->getOrientation() * Vector3( 0, 100 * elapsedTime, 0 ) );

	if( input->isKeyDown( OIS::KC_E ) )  mMainNode->pitch (Radian (2 * elapsedTime));
*/
    //mMainNode->translate( mMainNode->getOrientation() * Vector3( 0, -100 * elapsedTime, 0 ) );
    
    

	/* handle the shooting */
	shoot_timer -= elapsedTime;
	if( input->isKeyDown( OIS::KC_SPACE ) && shoot_timer <= 0 )
	{
	/*	if( !beam_stack.empty() )
		{
			beam_iter = beam_stack.end() - 1;

			(*beam_iter)->set_position(mMainNode->getPosition(), mMainNode->getOrientation());


			active_beams.push_back(*beam_iter);
			beam_stack.pop_back();
		}*/
        Ogre::String name = "beam_" + StringConverter::toString(i);
        BeamWeapon *test = new BeamWeapon(name, mWorld );
        test->set_position(mBody2->getPosition(), mBody2->getOrientation());
        test->create_beam();
		test->get_geometry()->setUserObject(static_cast<CollisionTestedObject*>(this));
        //beam_stack.push_back(beam_ent);
        //beam_stack.push_back(beam_ent);
        //
       // fok->addForce(Vector3(4,4,4));
        /* ROBOTTT 2 */

        i++;
        shoot_timer = 0.5;
	}

	if( !active_beams.empty() )
	{
        for( beam_iter = active_beams.begin(); beam_iter != active_beams.end(); beam_iter++)
		{
			if( (*beam_iter)->get_updates() <= 2000000 ) { (*beam_iter)->update_beam(); }
			else
			{
                (*beam_iter)->set_not_visible();
				active_beams.pop_back();
                beam_stack.push_back(*beam_iter);	
			}			
		}
    }
    
    Ogre::Real time = 0.1;
    if (mStepper->step(time))
    {
        mWorld->synchronise();
    }
	
	create_powerup(mBody2->getPosition());
	
}

bool OgreCharacter::collision(OgreOde::Contact *Contact)
{
    // Check for collisions between things that are connected and ignore them
    OgreOde::Geometry * const g1 = Contact->getFirstGeometry();
    OgreOde::Geometry * const g2 = Contact->getSecondGeometry();

    if (g1 && g2)
    {
        OgreOde::Body * b1 = g2->getBody();
        OgreOde::Body * b2 = g1->getBody();
	//	Ogre::SceneNode *node = mSceneMgr->getRootSceneNode()->getSceneNode(b1->getName());

		std::cout << "Collision node " << b2->getName() << " with node " << b1->getName() << std::endl;
		try{ 
			delete_list.clear();
			//your code with the node lookup 
			if (b1->getName().compare(0,5,"enemy") == 0) 
			{
				delete_list.push_back(g2);
			}
			if (b2->getName().compare(0,5,"enemy") == 0) 
			{
				delete_list.push_back(g1);
			}
			
			if ((b1->getName().compare(0,6,"player") == 0) && (b2->getName().compare(0,5,"boost") == 0) ||
				(b2->getName().compare(0,6,"player") == 0) && (b1->getName().compare(0,5,"boost") == 0)) 
			{
				boost = 5;
				boost_timer += 2;
				std::cout << "Boost!!!!" << std::endl;
			}
			if (( (b1->getName().compare(0,6,"player") == 0) && (b2->getName().compare(0,5,"enemy") == 0))  ||
				( (b1->getName().compare(0,5,"enemy") == 0) && (b2->getName().compare(0,6,"player") == 0) ))
			{
				lives--;
				
				delete_list.clear();
				
				if(b1->getName().compare(0,5,"enemy") == 0) delete_list.push_back(g2);
				else delete_list.push_back(g1);
					
			}
		} 
		catch (Ogre::Exception& e) 
		{ 
		//u dont have the node already registered in the scene manager 
		}


        if (b1 && b2 && OgreOde::Joint::areConnected(b1, b2)) 
            return false; 
    }

    //set contact parameters:
  /*  Contact->setBouncyness(1.0);
    Contact->setCoulombFriction(OgreOde::Utility::Infinity);
    Contact->setForceDependentSlip(1.0);
    Contact->setAdditionalFDS(1.0);*/

    /*we have 2 collidable objects from our object system, if one of the Collide function returns false, we return false in this method, too,
    else we return true, so ode computes a normal collision.
    true means ode will treat this like a normal collison => rigid body behavior
    false means ode will not treat this collision at all => objects ignore each other*/
    
    bool Return = true;
    
    if (g1->getUserObject())
        if (!static_cast<CollisionTestedObject*>(g1->getUserObject())->Collide(true, Contact))
            Return = false;
    
    if (g2->getUserObject())
        if (!static_cast<CollisionTestedObject*>(g2->getUserObject())->Collide(false, Contact))
            Return = false;
    
    return Return;
}

bool CollidingObject::Collide(bool MineIsFirst, OgreOde::Contact *Contact)
{
	printf("Ehhhh\n");/*
    /*Contact->setForceDependentSlip(Contact->getForceDependentSlip() * ForceDependentSlip);
    Contact->setAdditionalFDS(Contact->getForceDependentSlip2() * ForceDependentSlip);
    Contact->setCoulombFriction(Contact->getCoulombFrictionMu() * Friction);
    Contact->setBouncyness(Contact->getBouncyness() * Bouncyness, Contact->getBouncynessVelocity() * BounceVelocity);*/
    return true;
}


void OgreCharacter::set_info_manager(InfoManager* info_manager)
{
	this->info_manager = info_manager;
}


void OgreCharacter::create_powerup(Vector3 ogre_pos)
{
	if( (int) Ogre::Math::RangeRandom(0, 1000) == 1 ) 
	{
		mEntity = mSceneMgr->createEntity ("boost_e_" + StringConverter::toString (powerup_counter), "ball.mesh");
		mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("boost_" + StringConverter::toString (powerup_counter));
		mNode->attachObject(mEntity);
		mBody = new OgreOde::Body(mWorld, "boost_" + StringConverter::toString (powerup_counter));
		mNode->attachObject(mBody);
		Vector3 size(150.0,150.0,150.0);
		mGeom = (OgreOde::Geometry*)new OgreOde::BoxGeometry(size, mWorld, mSpace);
		mNode->setScale(size.x * 0.5,size.y * 0.5,size.z * 0.5);
				
		mGeom->setBody(mBody);
		mEntity->setUserObject(mGeom);
		mGeom->setUserObject(static_cast<CollisionTestedObject*>(this));
				
		//mBody->setPosition(Vector3( 175 * i, 0, 250 * j ));
		
		Vector3 position;

        if ((int)Ogre::Math::RangeRandom(0, 2) == 1) {
            position = Vector3( ogre_pos.x + (int)Ogre::Math::RangeRandom(10, 1000),
                              ogre_pos.y + (int)Ogre::Math::RangeRandom(10, 1000),
                              ogre_pos.z + (int)Ogre::Math::RangeRandom(10, 1000));
        } else {
            position = Vector3( ogre_pos.x - (int)Ogre::Math::RangeRandom(10, 1000),
                              ogre_pos.y - (int)Ogre::Math::RangeRandom(10, 1000),
                              ogre_pos.z - (int)Ogre::Math::RangeRandom(10, 1000));
        }
		mBody->setPosition(position);

		powerup_counter++;
		printf("\nSpawned a new powerup!\n");		
    } 
}
